#include "TPlaca.h"
#include "TComando.h"
#include <string.h>

#define CMD_SALIDA "QUIT"
#define CMD_CONNECT "CONNECT"
#define CMD_DISCONNECT "DISCONNECT"
#define CMD_RECV "RECV"
#define CMD_SEND "SEND"
#define CMD_HELP "HELP"

#define MAIN_OK 0
#define MAIN_ERR_MEMORIA 1
#define MAIN_ERR_NOHAYCONECTADAS 2
#define MAIN_ERR_PLACANOENCONTRADA 3
#define SALIR 99

#define MSG_ERR_CMDDESCONOCIDO "ERROR: Comando desconocido. Escriba 'help' para obtener ayuda.\n\n"
#define MSG_HELP " CONNECT <D>            Conecta la placa de direccion <D> al medio\
                \n DISCONNECT <D>         Desconecta del medio la placa de direccion <D>\
                \n SEND <O> <D> <MENSAJE> Envia <MENSAJE> desde <O> a <D>\
                \n RECV                   Muestra el contenido del buffer de las placas conectadas al medio\
                \n HELP                   Muestra este texto\n\n"
#define MSG_BIENVENIDA "TP1: Abstraccion\nSimulador de Red de Computadoras\n\nEscriba 'help' para obtener ayuda.\n\n"

#define MSG_PLACA_ERR_RANGO "ERROR: Direccion de la placa fuera de rango.\n"
#define MSG_MEDIO_ERR_NOHAYCONECTADAS "ERROR: No hay placas conectadas al medio."

int ejecutar_connect(TComando* comando, TMedio* medio, TPlaca* placas_conectadas[MAX_PLACAS], int* cantidad_placas_conectadas) {
	TDireccion direccion;
	TPlaca* placa;
	char* dir[LONGITUD_MAXIMA];
	int codigo_error;
	
	placa = (TPlaca*) malloc(sizeof(TPlaca));
	
	/* Convierto la direccion ingresada por el usuario al tipo TDireccion */
	*dir = (char*) malloc(LONGITUD_MAXIMA);
	Comando_ObtenerArgumentos(comando, 0, dir);
	direccion = (TDireccion) atoi(*dir);
	free(*dir);

	if (DEBUG) printf("ejecutar_connect:\n direccion = %d\n placa = %x\n", direccion, placa);

	if (!(codigo_error = Placa_Crear(placa, direccion))) {
		if (DEBUG) printf("ejecutar_connect: Placa creada.\n");
		if (!(codigo_error = Placa_Conectar(placa, medio))) {
			if (DEBUG) printf("ejecutar_connect: Placa conectada.\n");
			placas_conectadas[*cantidad_placas_conectadas] = placa;
			(*cantidad_placas_conectadas)++;
		}
	}
	
	return codigo_error;	
}

int ejecutar_disconnect(TComando* comando, TMedio* medio, TPlaca* placas_conectadas[MAX_PLACAS]) {
	/* TODO: Corregir esto */
	TDireccion direccion;
	char* dir[LONGITUD_MAXIMA];
	int codigo_error, cantidad_conectadas = 0, i = 0, j = 0, k = 0, a = 0;
	*dir = (char*) malloc(LONGITUD_MAXIMA);
	
	if ((cantidad_conectadas = Medio_ContarConectadas(medio)) > 0) {
		Comando_ObtenerArgumentos(comando, 0, dir);
		direccion = (TDireccion) atoi(*dir);
		while (k < (cantidad_conectadas - 1)) {
		    if (Placa_GetDireccion(placas_conectadas[k]) == direccion) {
		        Placa_Desconectar(placas_conectadas[k]);
		        i--;
		        cantidad_conectadas = Medio_ContarConectadas(medio);
		        for (j = k; j < cantidad_conectadas; j++) {
		            placas_conectadas[j] = placas_conectadas [j + 1];
		        }
		        a = 0;
		    } 
		    else
		        k++;
		}
		if (!a) {
		    placas_conectadas[Medio_ContarConectadas(medio) - 1] = NULL;
		    codigo_error = MAIN_OK;
		}
		else
		    if (Placa_GetDireccion(placas_conectadas[Medio_ContarConectadas(medio) - 1]) == direccion) {
		        Placa_Desconectar(placas_conectadas[Medio_ContarConectadas(medio) - 1]);
		        placas_conectadas[Medio_ContarConectadas(medio  - 1)] = NULL;
		        i--;
		        codigo_error = MAIN_OK;
		    }
		    else
		        printf ("ERROR");
	}
	else
		codigo_error = MAIN_ERR_NOHAYCONECTADAS;
	
	free(*dir);
	return codigo_error;
}

int ejecutar_send(TComando* comando, TMedio* medio, TPlaca* placas_conectadas[MAX_PLACAS], int cantidad_placas_conectadas) {
	int tam, i;
	TDireccion origen, destino;
	TPlaca* placa;
	char *s_origen[3], *s_destino[3], *mensaje[MTU];

	Comando_ObtenerArgumentos(comando, 0, s_origen);
	Comando_ObtenerArgumentos(comando, 1, s_destino);
	Comando_ObtenerArgumentos(comando, 2, mensaje);
	tam = strlen(*mensaje);
	destino = atoi(*s_destino);
	if (DEBUG) printf("ejecutar_send:\n *s_origen = %s\n *s_destino = %s\n *mensaje = %s\n tam = %d, destino = %d", *s_origen, *s_destino, *mensaje, tam, destino);
	/* Obtengo placa segun direccion de origen */
	if (DEBUG) printf("ejecutar_send: cantidad_placas_conectadas = %d\n", cantidad_placas_conectadas);
	placa = NULL;
	for (i = 0; i < cantidad_placas_conectadas; i++) {
		if (DEBUG) printf("ejecutar_send: placas_conectadas[%d] = %x\n", i, placas_conectadas[i]);
		origen = atoi(*s_origen);
		if (Placa_GetDireccion(placas_conectadas[i]) == origen) {
			placa = placas_conectadas[i];
			break;
		}
	}

	if (placa != NULL)
		return Placa_Enviar(placa, *mensaje, tam, destino);
	else
		return MAIN_ERR_PLACANOENCONTRADA;
}

int ejecutar_recv(TComando* comando, TMedio* medio, TPlaca* placas_conectadas[MAX_PLACAS]) {
	int codigo_error, cantidad_placas_conectadas, i;
	char* destino;
	
	cantidad_placas_conectadas = Medio_ContarConectadas(medio);
    destino = (char*)malloc(MTU);
    for (i = 0; i < cantidad_placas_conectadas; i++) {
        if (Placa_Leer(placas_conectadas[i], destino)) {
            printf ("Mensaje a leer de la placa con direccion %d: ",Placa_GetDireccion(placas_conectadas[i]));
            printf ("%s\n",(char*)destino);
        }
        else
            printf ("No hay mensaje en la placa de direccion %d\n",Placa_GetDireccion(placas_conectadas[i]));
    }
    free(destino);

	return codigo_error;
}

int interpretar_comando(TComando* comando, char* operacion, TMedio* medio, TPlaca* placas_conectadas[MAX_PLACAS], int* cantidad_placas_conectadas) {
	int salida;
	char* argumentos[LONGITUD_MAXIMA];

	*argumentos = (char*) malloc(LONGITUD_MAXIMA);

	Comando_Leer(comando);
	Comando_ObtenerOperacion(comando, &operacion);

	if (!strcasecmp(operacion, "HOLA")) {
		Comando_ObtenerArgumentos(comando, 0, argumentos);
		printf("Hola, %s! :-)\n", *argumentos);		
	}
	else if (!strcasecmp(operacion, CMD_CONNECT)) {
		salida = ejecutar_connect(comando, medio, placas_conectadas, cantidad_placas_conectadas);
		if (DEBUG) printf("interpretar_comando:\n salida = %d\n *cantidad_placas_conectadas = %d\n", salida, *cantidad_placas_conectadas);
		switch (salida) {
			case 1: printf(MSG_PLACA_ERR_RANGO);
		}
	}
	/* TODO: leer salida y escribir mensajes de error */
	else if (!strcasecmp(operacion, CMD_DISCONNECT)) {
		salida = ejecutar_disconnect(comando, medio, placas_conectadas);	
	}
	else if (!strcasecmp(operacion, CMD_SEND)) {
		salida = ejecutar_send(comando, medio, placas_conectadas, *cantidad_placas_conectadas);
	}
	else if (!strcasecmp(operacion, CMD_RECV)) {
		salida = ejecutar_recv(comando, medio, placas_conectadas);
	}
	else if (!strcasecmp(operacion, CMD_HELP)) {
		printf(MSG_HELP);	
	}
	else if (!strcasecmp(operacion, CMD_SALIDA)) {
		return SALIR;
	}
	else {
		printf(MSG_ERR_CMDDESCONOCIDO);
	}
	
	return 0;
}

int mostrar_consola() {
	TMedio medio;
	TComando comando;
	TPlaca* placas_conectadas[MAX_PLACAS];
	char* operacion[LONGITUD_MAXIMA];
	char* argumentos[LONGITUD_MAXIMA];
	int cantidad_placas_conectadas = 0, flag_salida = 0;

	if (Comando_Crear(&comando, LONGITUD_MAXIMA) != MAIN_OK)
		return MAIN_ERR_MEMORIA;
	
	*operacion = (char*) malloc(LONGITUD_MAXIMA);
	*argumentos = (char*) malloc(LONGITUD_MAXIMA);

	Medio_Crear(&medio);
	
	while (!flag_salida) {
		flag_salida = interpretar_comando(&comando, *operacion, &medio, placas_conectadas, &cantidad_placas_conectadas);
	}

	Comando_Destruir(&comando);
	free(*operacion);
	free(*argumentos);
	
	return MAIN_OK;
}

int main(int argc, char* argv[]) {
	printf(MSG_BIENVENIDA);
	mostrar_consola();
	return MAIN_OK;
}

/*** Empiezan casos de prueba ***/
int test() {	
	int codigo_error;
	TMedio medio;
	TPlaca placa;
	codigo_error = Medio_Crear(&medio);
	printf("main: &placa = %x\n", &placa);
	printf("main: Medio_Crear(medio) devolvio %d\n", codigo_error);
	printf("main: medio.cantidad_placas_conectadas = %d\n", medio.cantidad_placas_conectadas);
	
	codigo_error = Placa_Crear(&placa, 124);
	printf("main: Placa_Crear(placa) devolvio %d\n", codigo_error);
	
	printf("main: placa.direccion = %d\n", Placa_GetDireccion(&placa));
	
	codigo_error = Medio_Conectar(&medio, &placa);
	printf("main: Medio_Conectar(medio, placa) devolvio %d\n", codigo_error);
	/*printf("main: medio.placas_conectadas[0]: %d\n", Placa_GetDireccion(p));*/ /* No se por que pincha esto! */
	codigo_error = Placa_Recibir(&placa, "123 122 Hola que tal", 100);
	printf("main: Placa_Recibir salio con codigo %d\n", codigo_error);

	
	

	return MAIN_OK;
}
/*** Fin casos de prueba ***/
